package com.xhj.mybatis.executor.statement;

import com.xhj.mybatis.executor.Executor;
import com.xhj.mybatis.executor.keygen.KeyGenerator;
import com.xhj.mybatis.executor.parameter.ParameterHandler;
import com.xhj.mybatis.executor.resultset.ResultSetHandler;
import com.xhj.mybatis.mapping.BoundSql;
import com.xhj.mybatis.mapping.MappedStatement;
import com.xhj.mybatis.session.Configuration;
import com.xhj.mybatis.session.ResultHandler;
import com.xhj.mybatis.session.RowBounds;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * BaseStatementHandler
 *
 * @author XJks
 * @description 语句处理器抽象基类
 */
public abstract class BaseStatementHandler implements StatementHandler {

    /**
     * 全局配置对象，包含了所有配置信息，比如类型处理器、对象工厂等
     */
    protected final Configuration configuration;

    /**
     * 参数处理器，用于为 SQL 语句设置参数
     */
    protected final ParameterHandler parameterHandler;

    /**
     * 结果集处理器，用于处理 SQL 查询返回的结果
     */
    protected final ResultSetHandler resultSetHandler;

    /**
     * 执行器，负责执行 SQL 语句、管理事务等
     */
    protected final Executor executor;

    /**
     * 映射的 SQL 语句对象，包含 SQL、参数映射、结果映射等信息
     */
    protected final MappedStatement mappedStatement;

    /**
     * SQL 语句的参数对象，可以是单个值、JavaBean 或 Map 等
     */
    protected final Object parameterObject;

    /**
     * 封装了 SQL 语句和参数信息的对象
     */
    protected BoundSql boundSql;

    public BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        this.configuration = mappedStatement.getConfiguration();
        this.executor = executor;
        this.mappedStatement = mappedStatement;
        this.parameterObject = parameterObject;

        // 如果 boundSql 为空，说明还没有生成对应的 SQL 语句对象
        if (boundSql == null) {
            // TODO:先生成主键（如自增主键），有些数据库需要在生成 SQL 语句对象前处理主键
            generateKeys(parameterObject);
            // 通过参数对象生成 BoundSql，BoundSql 封装了 SQL 语句和参数信息
            boundSql = mappedStatement.getBoundSql(parameterObject);
        }

        this.boundSql = boundSql;
        this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
        this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    }

    @Override
    public Statement prepare(Connection connection) throws SQLException {
        // 声明 Statement 对象，初始为 null
        Statement statement = null;
        try {
            // 通过子类实现的方法实例化 Statement（如 PreparedStatement 或 Statement）
            statement = instantiateStatement(connection);
            // 返回准备好的 Statement 对象
            return statement;
        } catch (Exception e) {
            // 如果遇到其他异常，直接抛出异常
            throw new RuntimeException("Error preparing statement.  Cause: " + e, e);
        }
    }

    @Override
    public BoundSql getBoundSql() {
        return boundSql;
    }

    /**
     * 抽象方法：由子类实现，用于根据传入的数据库连接对象创建并返回一个 Statement 对象。
     * Statement 是 JDBC 执行 SQL 语句的接口，具体可以是 Statement、PreparedStatement 或 CallableStatement。
     *
     * @param connection 数据库连接对象，用于创建 Statement
     * @return Statement 返回创建好的 Statement 对象
     * @throws SQLException 如果创建 Statement 过程中发生数据库异常，则抛出该异常
     */
    protected abstract Statement instantiateStatement(Connection connection) throws SQLException;

    /**
     * 生成主键的方法。某些数据库（如 Oracle、MySQL）在插入数据前需要先生成主键（如自增ID或序列）。
     * 这个方法会在插入前处理主键相关逻辑。
     *
     * @param parameter 插入操作的参数对象，通常是要插入的数据
     */
    protected void generateKeys(Object parameter) {
        // 获取当前 SQL 映射语句对应的主键生成器（KeyGenerator），用于生成主键
        KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
        // 调用主键生成器的 processBefore 方法，在插入前生成主键
        keyGenerator.processBefore(executor, mappedStatement, null, parameter);
    }
}
